WindThunder Script Format
Archived from http://www.cinnamonpirate.com/docs/wtscript on 2006/2/7
Grudgingly copied and pasted from his browser by D

While this is a script format, and while it contains dialogue, ¡°script¡± in this example refers more to scripted actions. The extensions for this format are .SC0 or .SC1.

The scripts are generated by WindThunder¡¯s in-house event editor, a remaining dialogue of which can be found in Dialog 152 of wt_script_dll.dll. Unfortunately, the code for interacting with this form doesn¡¯t appear to be present in the packaged DLL.

The script structure is very complicated; currently, only enough to extract the text and rebuild the file is known.

Some script files have an 0¡Á1004 byte header. In all scripts with the header, it is exactly identical. I cannot remember anymore whether or not you actually need the header. To check if one exist, make sure 0¡Á1007 is not 0xcdcdcd, and 0¡Á1006 is not 0¡Á30000000. If both are not, then there is no header. Otherwise, the file begins at 0¡Á1004.

From that offset, the string loop begins; the actual string data immediately follows. All offsets are in little endian, and all strings are padded by 0¡Á00:

(Optional 0x1004 byte header)
String count [2 bytes, short]
(String loop)
  Tag [4 bytes, long (Unknown)]
  String offset [4 bytes, long]
  String length [4 bytes, long]
  String description [16 bytes, string (Usually ×Ö´®X)]
(End loop)
0x0000 [2 bytes, short]
(Data)
(Footer)

The data of the footer is currently known, but based on what the script editor in wt_script_dll.dll suggests, it¡¯s event code for what order things should happen and what events should loop. It likely calls the strings from the above block like an array.

All script files can safely be rebuilt by recalculating the string table and joining the original footer to the file.

A sample routine to extract all strings to separate lines is shown below. It¡¯s advised you use a termination marker, since dialogue lines use \n and \t:

$fd = fopen('3012104.sc0', 'rb');
fseek($fd, 0x1006, SEEK_SET);
$test2 = fread($fd, 4);
$test1 = subst($test2, 1);
if($test1==ord(0xcd).ord(0xcd).ord(0xcd) && $test2!=pack('V*', 0x30)) fseek($fd, 0, SEEK_SET);
else fseek($fd, 0x1004, SEEK_SET);
$count = unpack('v*', fread($fd, 2));
$tag = array();
$offset = array();
$length = array();
$descrip = array();
for($i=0; $i< $count; $i++) {
  $tag[$i] = unpack('N*', fread($fd, 4));
  $offset[$i] = unpack('V*', fread($fd, 4));
  $length[$i] = unpack('V*', fread($fd, 4));
  $descrip[$i] = rtrim(fread($fd, 16));
}
$null = fread($fd, 2);
for($i=0; $i<$count; $i++) {
  echo $descrip[$1].' ['.$tag[$i].']n';
  // Since strings run end-to-end we do not need to
  // fseek() to the next offset
  echo rtrim(fread($fd, $length[$i])).'nn';
}
fclose($fd);